//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using OpenLS.Core;
using OpenLS.Core.Serialization;
using OpenLS.Spreadsheet.Internals;

namespace OpenLS.Spreadsheet
{
    partial class Worksheet
    {
#if DEBUG
        public bool ValidAddresses
        {
            get
            {
                return this.Cells.NonVolatileCells.ValidAddresses;
            }
        }
#endif
        public void Merge(Range range)
        {
#if DEBUG
            Contract.Requires(range != null);
            Contract.Requires(ValidAddresses);
            Contract.Requires(IsValid);
            Contract.Ensures(ValidAddresses);
            Contract.Ensures(IsValid);
#endif
            if (range == null)
                throw new ArgumentNullException("Range");
            if (range.Worksheet != this)
                throw new ArgumentException("Range");
            if (range.Surface == 1)
                return;
            if (range is CellsRange)
            {
                var cellsRange = (CellsRange)range;
                using (CreateUndoScope(StringTable.Merge))
                {
                    MergeRange(cellsRange);
                    InvokeLayoutChanged(new DimensionEventArgs(null));
                }
            }
        }
        public void MergeAcross(Range s)
        {
            if (s.Surface == 1)
                return;
            //\\ using (CreateUndoScope("Merge"))
            {
                //\\ if (Workbook.UndoManager.IsEnabled)
                //\\ workbook.UndoManager.Add(new 
                if (s is CellsRange)
                {
                    var range = (CellsRange)s;
                    foreach (RangeAddress row in range.Address.GetRows())
                    {
                        Cell c = this[row.TopLeft];
                        var oldAddresses = new List<CellAddress>(c.GetRangeAddress().GetCellAddresses());
                        removeCellsInRange(CellsRange.Create(this, row), c, true);
                        this.SetCellSpan(c, row.RowSpan, row.ColumnSpan, oldAddresses);
                        //\\Cells_Changed(this, new EventArgs());
                    }
                    Cells.NonVolatileCells.NotifyCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
                }
                else
                {
                    throw new NotImplementedException();
                }
            }
        }

       /* private void arrangeCell(Cell cell, List<CellAddress> oldAddresses)
        {
            this.Cells.UpdateCellAddress(cell, oldAddresses, cell.GetRangeAddress().GetCellAddresses());
        }*/

        private void mergeCells(ReadContext context)
        {
            using (ReadContext c = context.Read("mergeCells"))
            {
                while (c.ReaderLocalName == "mergeCell")
                {
                    using (ReadContext c2 = c.Read("mergeCell"))
                    {
                        string rangeAddress = c2.GetString("ref");
                        RangeAddress address = GetRange(rangeAddress).Address;
                        var actualRange = CellsRange.Create(this, address);
                        mergeBorders(actualRange);
                        MergeRange(actualRange);
                    }
                }
            }
        }

        private static void mergeBorders(CellsRange range)
        {
            Cell topLeft = range.TopLeftCell;
            foreach (var cell in range.GetNonEmptyCells())
            {
                if (cell == topLeft)
                    continue;
                if (topLeft.Borders == null || topLeft.Borders.Bottom == null || topLeft.Borders.Bottom.Style == BorderStyle.None)
                {
                    if (cell.Address.Row == range.Row + range.RowSpan - 1)
                    {
                        if (cell.Borders != null && cell.Borders.Bottom != null)
                        {
                            var borders = topLeft.Borders;
                            if (borders == null)
                                borders = new Borders(null, null, null, cell.Borders.Bottom);
                            else
                            {
                                borders = new Borders(topLeft.Borders.Left, topLeft.Borders.Top, topLeft.Borders.Right,
                                                      cell.Borders.Bottom);

                            }
                            topLeft.Borders = borders;
                        }

                    }
                }
                if (topLeft.Borders == null || topLeft.Borders.Right == null || topLeft.Borders.Right.Style == BorderStyle.None)
                {
                    if (cell.Address.Column == range.Column + range.ColumnSpan - 1)
                    {
                        if (cell.Borders != null && cell.Borders.Right != null)
                        {
                            var borders = topLeft.Borders;
                            if (borders == null)
                                borders = new Borders(null, null, cell.Borders.Right, null);
                            else
                            {
                                borders = new Borders(topLeft.Borders.Left, topLeft.Borders.Top, cell.Borders.Right,
                                                      topLeft.Borders.Bottom);

                            }
                            topLeft.Borders = borders;
                        }

                    }
                }
            }
        }

        internal void Unmerge(Range s)
        {
#if DEBUG
            Contract.Requires(ValidAddresses);
            Contract.Ensures(ValidAddresses);
#endif
            using (CreateUndoScope(StringTable.Merge))
            {
               // if (Workbook.UndoEnabled)
                 //   Workbook.AddUndoUnit(new UnmergeRangeUndoUnit(s));
                if (s is CellsRange)
                {
                    var range = (CellsRange)s;
                    var addresses = range.Address.GetCellAddresses();
                    var cells = Enumerable.Select(addresses, a => this.NonVolatileCells.GetItemIfAny(a));
                    cells = Enumerable.Where(cells, c => c != null && c.HasSpan);
                    List<Cell> cellList = Enumerable.ToList(cells);
                    foreach (var cell in cellList)
                    {
                        this.Cells.BeginEdit();
                        this.SetCellSpan(cell, 1, 1);
                        Cells.EndEdit();
                    }
                }
                else
                {
                    throw new NotImplementedException();
                }
                InvokeLayoutChanged(new DimensionEventArgs(null));
            }
        }
        void SetCellSpan (Cell cell, int rowSpan, int columnSpan)
        {
            var oldAddresses = Enumerable.ToList(cell.GetRangeAddress().GetCellAddresses());
            SetCellSpan(cell, rowSpan, columnSpan, oldAddresses);
//            cell.RowSpan = rowSpan;
  //          cell.ColumnSpan = columnSpan;
    //        Cells.UpdateCellAddress(cell, oldAddresses, cell.GetRangeAddress().GetCellAddresses());
        }
        
        internal void SetCellSpan(Cell cell, int rowSpan, int columnSpan, IEnumerable<CellAddress>oldAddresses)
        {
            if (Workbook.UndoEnabled)
                Workbook.AddUndoUnit(new SetCellSpanUndoUnit(cell));
            cell.RowSpan = rowSpan;
            cell.ColumnSpan = columnSpan;
            Cells.UpdateCellAddress(cell, oldAddresses, cell.GetRangeAddress().GetCellAddresses());
            if (!cell.IsVolatile)
            {
                foreach (var a in oldAddresses)
                {
                    Rows[a.Row].RemoveAt(a.Column);
                    Columns[a.Column].RemoveAt(a.Row);
                }
                for (int r = cell.Row.Index; r < cell.Row.Index + rowSpan; r++)
                {
                    var row = Rows[r];
                    row.Add(cell);
                }
                for (int c = cell.Column.Index; c < cell.Column.Index + columnSpan; c++)
                {
                    var column = Columns[c];
                    column.Add(cell);
                }
            }
        }

        internal void MergeRange(CellsRange range)
        {
#if DEBUG
            Contract.Requires(IsValid);
            Contract.Ensures(IsValid);
#endif
            //if (Workbook.UndoEnabled)
              //  Workbook.AddUndoUnit(new MergeRangeUndoUnit(range));
            Cell c = this[range.Address.TopLeft];
            var oldAddresses = new List<CellAddress>(c.GetRangeAddress().GetCellAddresses());
            if (c.IsVolatile)
            {
                c.Value = null;
                // c.SetNonVolatile();
            }
            List<Cell> removedCells = Enumerable.ToList(range.GetNonEmptyCells());
            removeCellsInRange(range, c, true);
            OnCellsRemoved(new MultiCellRegion(removedCells));
            /*
            Contract.Assert(!Enumerable.Any(range.GetRows(), r => r.Contains(range.TopLeftCell)));
            Contract.Assert(c.Row != null);
            Contract.Assert(!Enumerable.Any( c.Row.GetNonEmptyCells(), cc => cc == c));
            Contract.Assert(!Enumerable.Any(range.GetRows(), r => r.Contains(range.TopLeftCell)));
            Contract.Assert(!Enumerable.Any(range.GetRows(), r => r.Contains(range.TopLeftCell)));
            Contract.Assert(!Enumerable.Any(range.GetColumns(), r => r.Contains(range.TopLeftCell)));
            */
            bool mergeValid = (!Enumerable.Any(range.GetRows(), r => r.Contains(range.TopLeftCell)));
            if (!mergeValid) return;

            mergeValid = (c.Row != null);
            if (!mergeValid) return;

            mergeValid = (!Enumerable.Any(c.Row.GetNonEmptyCells(), cc => cc == c));
            if (!mergeValid) return;

            mergeValid = (!Enumerable.Any(range.GetRows(), r => r.Contains(range.TopLeftCell)));
            if (!mergeValid) return;

            mergeValid = (!Enumerable.Any(range.GetRows(), r => r.Contains(range.TopLeftCell)));
            if (!mergeValid) return;

            this.SetCellSpan(c, range.RowSpan, range.ColumnSpan);
//            c.RowSpan = range.RowSpan;
  //          c.ColumnSpan = range.ColumnSpan;
            //\\c.Span = range.Address.Span;

    //        Contract.Assert(!Enumerable.Any(c.Row.GetNonEmptyCells(), cc => cc == c));
      //      this.Cells.UpdateCellAddress(c, oldAddresses, c.GetRangeAddress().GetCellAddresses());
            
            Cells.NonVolatileCells.NotifyCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
            OnCellAdded(c);

          //  addCellWithUndo(c);
          //  c.Undelete();
        }

    }
}
